package com.ywz.framework.security;

import com.ywz.framework.security.service.AccessDeniedHandlerImpl;
import com.ywz.framework.security.service.AuthenticationEntryPointImpl;
import com.ywz.framework.sso.SSOAuthenticationManager;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import static org.springframework.security.config.Customizer.withDefaults;


/**
 * 类描述 -> Spring Security 配置类
 *
 * @Author: ywz
 * @Date: 2024/09/27
 */
@Configuration
@EnableWebSecurity // 是开启SpringSecurity的默认行为
@EnableMethodSecurity // 开启方法级别的权限注解，Security6新增
public class SecurityConfig {
    @Value("${sso.enabled}")
    private boolean SSO_ENABLE = false;
    @Resource
    private AccessDeniedHandlerImpl accessDeniedHandler;
    @Resource
    private AuthenticationEntryPointImpl authenticationEntryPoint;
    @Resource
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    // 放行接口（登录、注册、获取验证码、忘记密码、下载图片）
    private static final String[] IGNORE_URI = {"/system/login", "/system/register", "/system/captcha", "/system/forgetPassword", "/image/downloadImage", "/file/downloadFile"};
    // swagger静态资源放行
    private static final String[] SWAGGER_URIS = {"/swagger-resources/**", "/swagger-ui/**", "/v3/**", "/error"};

    /**
     * 方法描述 -> 密码明文加密方式配置
     *
     * @Author: ywz
     * @Date: 2024/07/28
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 方法描述 -> 获取AuthenticationManager（认证管理器），登录时认证使用
     *
     * @param authenticationConfiguration 认证配置
     * @Author: ywz
     * @Date: 2024/07/28
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        if (SSO_ENABLE)
            return new SSOAuthenticationManager();
        return authenticationConfiguration.getAuthenticationManager();
    }


    /**
     * 方法描述 -> 获取SecurityFilterChain（过滤器链），配置过滤器
     *
     * @param http httpSecurity
     * @Author: ywz
     * @Date: 2024/07/28
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authorize -> {
                            // 放行接口
                            authorize.requestMatchers(IGNORE_URI).permitAll();
                            authorize.requestMatchers(SWAGGER_URIS).permitAll();
                            // 其他接口需要认证
                            authorize.anyRequest().authenticated();
                        }
                ).csrf(AbstractHttpConfigurer::disable) // 基于token，不需要csrf
                // 保证jwt在认证前执行
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
                // 基于token，不需要session
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .exceptionHandling(exception -> { // 认证失败处理器
                    exception.accessDeniedHandler(accessDeniedHandler);
                    exception.authenticationEntryPoint(authenticationEntryPoint);
                })
                .httpBasic(withDefaults())
                .formLogin(withDefaults());
        return http.build();
    }

}